home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 February / EnigmA AMIGA RUN 04 (1996)(G.R. Edizioni)(IT)[!][issue 1996-02][Skylink CD III].iso / earcd / comm2 / termsorc.lha / Extras / Source / term-source.lha / termPacket.c < prev    next >
C/C++ Source or Header  |  1995-09-26  |  28KB  |  1,240 lines

  1. /*
  2. **    termPacket.c
  3. **
  4. **    Support routines for the `packet window'
  5. **
  6. **    Copyright © 1990-1995 by Olaf `Olsen' Barthel
  7. **        All Rights Reserved
  8. */
  9.  
  10. #include "termGlobal.h"
  11.  
  12.     /* A custom message structure. */
  13.  
  14. struct PacketMessage
  15. {
  16.     struct Message     VanillaMessage;
  17.     LONG         Line;
  18. };
  19.  
  20.     /* Some private data required to handle both the window,
  21.      * the editing functions and the command history buffer.
  22.      */
  23.  
  24. STATIC struct Hook         PacketHook;
  25. STATIC STRPTR             PacketContents;
  26.  
  27. STATIC struct Process        *PacketProcess;
  28. STATIC struct SignalSemaphore     PacketSemaphore;
  29. STATIC struct MsgPort        *PacketPort;
  30.  
  31. STATIC LONG             PacketLine;
  32. STATIC LONG             PacketCount;
  33. STATIC STRPTR             PacketString    = NULL;
  34. STATIC LONG             PacketX    = -1,
  35.                  PacketY    = -1,
  36.                  PacketWidth    = -1,
  37.                  PacketHeight    = -1;
  38.  
  39. STATIC BYTE             HasList    = FALSE,
  40.                  Echo        = -1;
  41.                  PacketSignal    = -1;
  42.  
  43.     /* Gadget IDs */
  44.  
  45. enum    {    GAD_STRING, GAD_LIST };
  46.  
  47.     /* The menu to attach to the packet window. */
  48.  
  49. enum    {    MEN_LOADHISTORY=1,MEN_SAVEHISTORY,MEN_CLEARHISTORY,MEN_OTHERWINDOW,
  50.         MEN_TOGGLE_ECHO,MEN_QUITPANEL
  51.     };
  52.  
  53. STATIC struct NewMenu NewPacketMenu[] =
  54. {
  55.     { NM_TITLE, NULL,         0 , 0,        0, (APTR)0},
  56.     {  NM_ITEM, NULL,         0 , 0,        0, (APTR)MEN_LOADHISTORY},
  57.     {  NM_ITEM, NULL,         0 , 0,        0, (APTR)MEN_SAVEHISTORY},
  58.     {  NM_ITEM, NULL,         0 , 0,        0, (APTR)MEN_CLEARHISTORY},
  59.     {  NM_ITEM, NM_BARLABEL,     0 , 0,        0, (APTR)0},
  60.     {  NM_ITEM, NULL,         0 , 0,        0, (APTR)MEN_OTHERWINDOW},
  61.     {  NM_ITEM, NM_BARLABEL,     0 , 0,        0, (APTR)0},
  62.     {  NM_ITEM, NULL,         0 , TICK,    0, (APTR)MEN_TOGGLE_ECHO},
  63.     {  NM_ITEM, NM_BARLABEL,     0 , 0,        0, (APTR)0},
  64.     {  NM_ITEM, NULL,         0 , 0,        0, (APTR)MEN_QUITPANEL},
  65.     { NM_END, 0,             0 , 0,        0, (APTR)0}
  66. };
  67.  
  68. STATIC VOID __stdargs
  69. PacketDestructor(struct MsgItem *Item)
  70. {
  71.     Signal(PacketProcess,1L << PacketSignal);
  72. }
  73.  
  74. STATIC VOID __regargs
  75. PacketSerWrite(APTR Data,LONG Size)
  76. {
  77.     struct DataMsg Msg;
  78.  
  79.     InitMsgItem(&Msg,PacketDestructor);
  80.  
  81.     Msg . Type = DATAMSGTYPE_WRITE;
  82.     Msg . Data = Data;
  83.     Msg . Size = Size;
  84.  
  85.     Forbid();
  86.  
  87.     ClrSignal(1L << PacketSignal);
  88.  
  89.     PutMsgItem(SpecialQueue,(struct MsgItem *)&Msg);
  90.  
  91.     Wait(1L << PacketSignal);
  92.  
  93.     Permit();
  94. }
  95.  
  96. STATIC VOID __regargs
  97. PacketSerialCommand(STRPTR String)
  98. {
  99.     struct DataMsg Msg;
  100.  
  101.     InitMsgItem(&Msg,PacketDestructor);
  102.  
  103.     if(Echo)
  104.         Msg . Type = DATAMSGTYPE_SERIALCOMMAND;
  105.     else
  106.         Msg . Type = DATAMSGTYPE_SERIALCOMMANDNOECHO;
  107.  
  108.     Msg . Data = String;
  109.  
  110.     Forbid();
  111.  
  112.     ClrSignal(1L << PacketSignal);
  113.  
  114.     PutMsgItem(SpecialQueue,(struct MsgItem *)&Msg);
  115.  
  116.     Wait(1L << PacketSignal);
  117.  
  118.     Permit();
  119. }
  120.  
  121.     /* SendPacketMsg(LONG Line):
  122.      *
  123.      *    Tell the main process to display a certain line.
  124.      */
  125.  
  126. STATIC VOID __regargs
  127. SendPacketMsg(LONG Line)
  128. {
  129.     struct PacketMessage *Msg;
  130.  
  131.     if(Msg = (struct PacketMessage *)AllocVecPooled(sizeof(struct PacketMessage),MEMF_PUBLIC | MEMF_CLEAR))
  132.     {
  133.         Msg -> VanillaMessage . mn_Length    = sizeof(struct PacketMessage);
  134.         Msg -> Line                = Line;
  135.  
  136.         PutMsg(PacketPort,Msg);
  137.     }
  138. }
  139.  
  140.     /* CreateAllGadgets():
  141.      *
  142.      *    Create the packet string gadget.
  143.      */
  144.  
  145. STATIC struct Gadget * __regargs
  146. CreateAllGadgets(LONG Width,struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo)
  147. {
  148.     struct Gadget        *Gadget;
  149.     struct NewGadget     NewGadget;
  150.  
  151.     ObtainSemaphore(&PacketSemaphore);
  152.  
  153.     memset(&NewGadget,0,sizeof(struct NewGadget));
  154.  
  155.     if(Gadget = CreateContext(GadgetList))
  156.     {
  157.         WORD Rest,StringHeight,ProtoHeight;
  158.  
  159.         StringHeight    = 3 + TextAttr . tta_YSize + 3;
  160.         ProtoHeight    = Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 1 + 1 + StringHeight + 1 + Window -> WScreen -> WBorBottom;
  161.         Rest        = (PacketHeight - 2 - ProtoHeight - 2) / TextAttr . tta_YSize;
  162.  
  163.         NewGadget . ng_Width        = Width - 26;
  164.         NewGadget . ng_Height        = StringHeight;
  165.         NewGadget . ng_GadgetText    = NULL;
  166.         NewGadget . ng_TextAttr        = &TextAttr;
  167.         NewGadget . ng_VisualInfo    = VisualInfo;
  168.         NewGadget . ng_GadgetID        = GAD_STRING;
  169.         NewGadget . ng_Flags        = 0;
  170.         NewGadget . ng_LeftEdge        = 6;
  171.         NewGadget . ng_TopEdge        = Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 2;
  172.  
  173.         GadgetArray[GAD_STRING] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
  174.             GTST_MaxChars,    1024,
  175.             GTST_EditHook,    &PacketHook,
  176.             GA_TabCycle,    FALSE,
  177.         TAG_DONE);
  178.  
  179.             /* If the window is large enough to display the
  180.              * list, create the list view gadget.
  181.              */
  182.  
  183.         if(Rest > 0)
  184.         {
  185.             NewGadget . ng_Height        = 2 + Rest * TextAttr . tta_YSize + 2 + StringHeight;
  186.             NewGadget . ng_GadgetID        = GAD_LIST;
  187.  
  188.             GadgetArray[GAD_LIST] = Gadget = CreateGadget(LISTVIEW_KIND,Gadget,&NewGadget,
  189.                 GTLV_ShowSelected,    GadgetArray[GAD_STRING],
  190.                 GTLV_Labels,        &PacketHistoryList,
  191.                 GTLV_Selected,        PacketCount,
  192.                 GTLV_Top,        PacketCount,
  193.             TAG_DONE);
  194.  
  195.             if(Gadget)
  196.                 HasList = TRUE;
  197.             else
  198.                 HasList = FALSE;
  199.         }
  200.         else
  201.             HasList = FALSE;
  202.     }
  203.  
  204.     ReleaseSemaphore(&PacketSemaphore);
  205.  
  206.     return(Gadget);
  207. }
  208.  
  209.     /* PacketKey(struct Hook *Hook,struct SGWork *Work,ULONG *Msg):
  210.      *
  211.      *    This is a string gadget editing call back routine
  212.      *    (a so-called `hook') which is to perform all the
  213.      *    line editing and command history actions supported
  214.      *    by the packet window.
  215.      */
  216.  
  217. STATIC ULONG __saveds __asm
  218. PacketKey(register __a0 struct Hook *Hook,register __a1 ULONG *Msg,register __a2 struct SGWork *Work)
  219. {
  220.         /* Someone activated the string gadget and
  221.          * hit a key.
  222.          */
  223.  
  224.     if(*Msg == SGH_KEY)
  225.     {
  226.             /* Right-Amiga-key was pressed, release the
  227.              * string gadget so user may select a menu
  228.              * item.
  229.              */
  230.  
  231.         if((Work -> IEvent -> ie_Qualifier & AMIGARIGHT) && Work -> IEvent -> ie_Code < 96)
  232.         {
  233.             if(!(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) && (Work -> IEvent -> ie_Code == KEYCODE_X || Work -> IEvent -> ie_Code == KEYCODE_Q))
  234.                 return(TRUE);
  235.             else
  236.             {
  237.                 Work -> Actions = (Work -> Actions & ~(SGA_USE | SGA_BEEP)) | SGA_END | SGA_REUSE;
  238.                 Work -> Code = '\t';
  239.  
  240.                 CommandWindow = Work -> GadgetInfo -> gi_Window;
  241.                 CommandGadget = Work -> Gadget;
  242.             }
  243.         }
  244.  
  245.         if(Work -> IEvent -> ie_Code >= F01_CODE && Work -> IEvent -> ie_Code <= F10_CODE)
  246.         {
  247.             STATIC UBYTE __far OtherBuffer[2048];
  248.  
  249.             STRPTR    String;
  250.             LONG    Len,Index = Work -> IEvent -> ie_Code - F01_CODE;
  251.  
  252.             Forbid();
  253.  
  254.             if(Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL)
  255.                 String = MacroKeys -> Keys[3][Index];
  256.             else
  257.             {
  258.                 if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
  259.                     String = MacroKeys -> Keys[2][Index];
  260.                 else
  261.                 {
  262.                     if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
  263.                         String = MacroKeys -> Keys[1][Index];
  264.                     else
  265.                         String = MacroKeys -> Keys[0][Index];
  266.                 }
  267.             }
  268.  
  269.             if(Len = strlen(String))
  270.             {
  271.                 if(Work -> BufferPos)
  272.                     CopyMem(Work -> PrevBuffer,OtherBuffer,Work -> BufferPos);
  273.  
  274.                 OtherBuffer[Work -> BufferPos] = 0;
  275.  
  276.                 strcat(OtherBuffer,String);
  277.  
  278.                 strcat(OtherBuffer,&Work -> PrevBuffer[Work -> BufferPos]);
  279.  
  280.                 if(strlen(OtherBuffer) > 1023)
  281.                     OtherBuffer[1023] = 0;
  282.  
  283.                 strcpy(Work -> WorkBuffer,OtherBuffer);
  284.  
  285.                 if(Work -> StringInfo -> UndoBuffer)
  286.                     strcpy(Work -> StringInfo -> UndoBuffer,Work -> PrevBuffer);
  287.  
  288.                 Work -> StringInfo -> UndoPos = Work -> BufferPos;
  289.  
  290.                 Work -> BufferPos    += Len;
  291.                 Work -> NumChars    += Len;
  292.  
  293.                 Work -> Actions = (Work -> Actions & ~SGA_BEEP) | SGA_USE | SGA_REDISPLAY;
  294.             }
  295.  
  296.             Permit();
  297.  
  298.             return(TRUE);
  299.         }
  300.  
  301.             /* The user pressed the cursor-right key to
  302.              * move the cursor to the next word in the buffer.
  303.              */
  304.  
  305.         if(Work -> IEvent -> ie_Code == CURSORRIGHT && (Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL))
  306.         {
  307.             if(Work -> BufferPos != Work -> NumChars)
  308.             {
  309.                 WORD i,Position = -1;
  310.  
  311.                 for(i = Work -> BufferPos ; i < Work -> NumChars ; i++)
  312.                 {
  313.                     if(Work -> WorkBuffer[i] == ' ')
  314.                     {
  315.                         for( ; i < Work -> NumChars ; i++)
  316.                         {
  317.                             if(Work -> WorkBuffer[i] != ' ')
  318.                             {
  319.                                 Position = i;
  320.                                 break;
  321.                             }
  322.                         }
  323.  
  324.                         break;
  325.                     }
  326.                 }
  327.  
  328.                 if(Position != -1)
  329.                     Work -> BufferPos = Position;
  330.                 else
  331.                     Work -> BufferPos = Work -> NumChars;
  332.  
  333.                 Work -> EditOp = EO_MOVECURSOR;
  334.             }
  335.  
  336.             return(TRUE);
  337.         }
  338.  
  339.             /* The user pressed the cursor-right key to
  340.              * move the cursor to the previous word in the buffer.
  341.              */
  342.  
  343.         if(Work -> IEvent -> ie_Code == CURSORLEFT && (Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL))
  344.         {
  345.             if(Work -> BufferPos)
  346.             {
  347.                 WORD i,Position = -1;
  348.  
  349.                 for(i = Work -> BufferPos ; i >= 0 ; i--)
  350.                 {
  351.                     if(Work -> WorkBuffer[i] != ' ')
  352.                     {
  353.                         Position = i;
  354.                         break;
  355.                     }
  356.                 }
  357.  
  358.                 if(Position == -1)
  359.                     Position = 0;
  360.  
  361.                 if(Position)
  362.                 {
  363.                     i = Position;
  364.  
  365.                     Position = -1;
  366.  
  367.                     for( ; i >= 0 ; i--)
  368.                     {
  369.                         if(Work -> WorkBuffer[i] == ' ')
  370.                         {
  371.                             Position = i + 1;
  372.                             break;
  373.                         }
  374.                     }
  375.                 }
  376.  
  377.                 if(Position != -1)
  378.                     Work -> BufferPos = Position;
  379.                 else
  380.                     Work -> BufferPos = 0;
  381.  
  382.                 Work -> EditOp = EO_MOVECURSOR;
  383.             }
  384.         }
  385.  
  386.             /* The user pressed the cursor-up key to
  387.              * scroll through the command history.
  388.              */
  389.  
  390.         if(Work -> IEvent -> ie_Code == CURSORUP)
  391.         {
  392.                 /* Shift key: jump to first command
  393.                  * history entry.
  394.                  */
  395.  
  396.             if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
  397.             {
  398.                 if(PacketLine)
  399.                     SendPacketMsg(0);
  400.             }
  401.             else
  402.             {
  403.                 if(PacketLine && PacketCount > 0)
  404.                     SendPacketMsg(PacketCount - 1);
  405.             }
  406.         }
  407.  
  408.             /* The user pressed the cursor-down key to
  409.              * scroll through the command history.
  410.              */
  411.  
  412.         if(Work -> IEvent -> ie_Code == CURSORDOWN)
  413.         {
  414.                 /* Shift key: jump to last command
  415.                  * history entry.
  416.                  */
  417.  
  418.             if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
  419.             {
  420.                 if(PacketLine > 0)
  421.                     SendPacketMsg(PacketLine);
  422.             }
  423.             else
  424.             {
  425.                 if(PacketCount < PacketLine)
  426.                     SendPacketMsg(PacketCount + 1);
  427.             }
  428.         }
  429.  
  430.         return(TRUE);
  431.     }
  432.     else
  433.     {
  434.         if(*Msg == SGH_CLICK)
  435.             return(TRUE);
  436.         else
  437.             return(FALSE);
  438.     }
  439. }
  440.  
  441.     /* HandlePacket():
  442.      *
  443.      *    Process the input coming through the packet window.
  444.      */
  445.  
  446. STATIC BOOLEAN
  447. HandlePacket(VOID)
  448. {
  449.     struct IntuiMessage    *Message;
  450.     struct PacketMessage    *PacketMsg;
  451.     ULONG             MsgClass,MsgCode;
  452.     struct Gadget        *MsgGadget;
  453.     struct FileRequester    *FileRequest;
  454.     UBYTE             DummyBuffer[MAX_FILENAME_LENGTH];
  455.     BOOLEAN             SwapWindow = FALSE;
  456.  
  457.     FOREVER
  458.     {
  459.         if(Wait(PORTMASK(PacketWindow -> UserPort) | SIG_KILL) & SIG_KILL)
  460.             return(TRUE);
  461.  
  462.         while(Message = (struct IntuiMessage *)GT_GetIMsg(PacketWindow -> UserPort))
  463.         {
  464.             MsgClass    = Message -> Class;
  465.             MsgCode        = Message -> Code;
  466.             MsgGadget    = (struct Gadget *)Message -> IAddress;
  467.  
  468.             GT_ReplyIMsg(Message);
  469.  
  470.                 /* Re-enable the string gadget if necessary. */
  471.  
  472.             if(MsgClass == IDCMP_RAWKEY)
  473.             {
  474.                 if(MsgCode == IECODE_UP_PREFIX|RAMIGA_CODE && CommandWindow == PacketWindow)
  475.                     ActivateGadget(CommandGadget,PacketWindow,NULL);
  476.  
  477.                 if(MsgCode == HELP_CODE)
  478.                     GuideDisplay(CONTEXT_PACKETWINDOW);
  479.             }
  480.  
  481.                 /* Menu help is required. */
  482.  
  483.             if(MsgClass == IDCMP_MENUHELP)
  484.                 GuideDisplay(CONTEXT_PACKET_MENU);
  485.  
  486.                 // Imagery may need refreshing
  487.  
  488.             if(MsgClass == IDCMP_REFRESHWINDOW)
  489.             {
  490.                 GT_BeginRefresh(PacketWindow);
  491.                 GT_EndRefresh(PacketWindow,TRUE);
  492.             }
  493.  
  494.                 /* Handle the menu. */
  495.  
  496.             if(MsgClass == IDCMP_MENUPICK)
  497.             {
  498.                 struct MenuItem *MenuItem;
  499.  
  500.                 while(MsgCode != MENUNULL)
  501.                 {
  502.                     MenuItem = ItemAddress(PacketMenu,MsgCode);
  503.  
  504.                     switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
  505.                     {
  506.                         case MEN_TOGGLE_ECHO:
  507.  
  508.                             if(MenuItem -> Flags & CHECKED)
  509.                                 Echo = TRUE;
  510.                             else
  511.                                 Echo = FALSE;
  512.  
  513.                             break;
  514.  
  515.                         case MEN_QUITPANEL:
  516.  
  517.                             MsgClass = IDCMP_CLOSEWINDOW;
  518.                             break;
  519.  
  520.                         case MEN_LOADHISTORY:
  521.  
  522.                             LT_LockWindow(PacketWindow);
  523.  
  524.                             if(FileRequest = GetFile(PacketWindow,LocaleString(MSG_PACKET_LOAD_HISTORY_TXT),"","",DummyBuffer,NULL,FALSE,FALSE,FALSE,LocaleString(MSG_GLOBAL_LOAD_TXT),FALSE))
  525.                             {
  526.                                 if(GetFileSize(DummyBuffer))
  527.                                 {
  528.                                     BPTR SomeFile;
  529.  
  530.                                     if(SomeFile = Open(DummyBuffer,MODE_OLDFILE))
  531.                                     {
  532.                                         if(PacketLine)
  533.                                         {
  534.                                             switch(MyEasyRequest(PacketWindow,LocaleString(MSG_PACKET_PACKET_WINDOW_STILL_HOLDS_LINES_TXT),LocaleString(MSG_PACKET_DISCARD_APPEND_CANCEL_TXT),PacketLine))
  535.                                             {
  536.                                                 case 1:    ClearPacketHistory();
  537.                                                     break;
  538.  
  539.                                                 case 0:    Close(SomeFile);
  540.                                                     SomeFile = NULL;
  541.                                                     break;
  542.                                             }
  543.                                         }
  544.  
  545.                                         if(SomeFile)
  546.                                         {
  547.                                             WORD Len;
  548.  
  549.                                             LineRead(NULL,NULL,NULL);
  550.  
  551.                                             while(Len = LineRead(SomeFile,DummyBuffer,255))
  552.                                             {
  553.                                                 DummyBuffer[Len - 1] = 0;
  554.  
  555.                                                 AddPacketHistory(DummyBuffer);
  556.                                             }
  557.  
  558.                                             Close(SomeFile);
  559.                                         }
  560.                                     }
  561.                                     else
  562.                                         ShowError(PacketWindow,ERR_LOAD_ERROR,IoErr(),DummyBuffer);
  563.                                 }
  564.  
  565.                                 FreeAslRequest(FileRequest);
  566.                             }
  567.  
  568.                             LT_UnlockWindow(PacketWindow);
  569.                             break;
  570.  
  571.                         case MEN_SAVEHISTORY:
  572.  
  573.                             LT_LockWindow(PacketWindow);
  574.  
  575.                             if(!PacketLine)
  576.                                 MyEasyRequest(PacketWindow,LocaleString(MSG_PACKET_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));
  577.                             else
  578.                             {
  579.                                 if(FileRequest = GetFile(PacketWindow,LocaleString(MSG_PACKET_SAVE_HISTORY_TXT),"","",DummyBuffer,NULL,TRUE,FALSE,FALSE,LocaleString(MSG_GLOBAL_SAVE_TXT),FALSE))
  580.                                 {
  581.                                     BPTR SomeFile = NULL;
  582.                                     LONG Error = 0;
  583.  
  584.                                         /* If the file we are about
  585.                                          * to create already exists,
  586.                                          * ask the user whether we are
  587.                                          * to create, append or skip
  588.                                          * the file.
  589.                                          */
  590.  
  591.                                     if(GetFileSize(DummyBuffer))
  592.                                     {
  593.                                         switch(MyEasyRequest(PacketWindow,LocaleString(MSG_GLOBAL_FILE_ALREADY_EXISTS_TXT),LocaleString(MSG_GLOBAL_CREATE_APPEND_CANCEL_TXT),DummyBuffer))
  594.                                         {
  595.                                             case 1:    SomeFile = Open(DummyBuffer,MODE_NEWFILE);
  596.                                                 break;
  597.  
  598.                                             case 2:    if(SomeFile = Open(DummyBuffer,MODE_READWRITE))
  599.                                                 {
  600.                                                     if(Seek(SomeFile,0,OFFSET_END) == -1)
  601.                                                     {
  602.                                                         Close(SomeFile);
  603.  
  604.                                                         SomeFile = NULL;
  605.                                                     }
  606.                                                 }
  607.                                                 break;
  608.                                         }
  609.                                     }
  610.                                     else
  611.                                         SomeFile = Open(DummyBuffer,MODE_NEWFILE);
  612.  
  613.                                     if(!SomeFile)
  614.                                         Error = IoErr();
  615.                                     else
  616.                                     {
  617.                                         struct Node *SomeNode;
  618.  
  619.                                         ObtainSemaphore(&PacketSemaphore);
  620.  
  621.                                         SomeNode = PacketHistoryList . lh_Head;
  622.  
  623.                                         while(SomeNode -> ln_Succ)
  624.                                         {
  625.                                             SetIoErr(0);
  626.  
  627.                                             if(FPrintf(SomeFile,"%s\n",SomeNode -> ln_Name) < 1)
  628.                                             {
  629.                                                 Error = IoErr();
  630.  
  631.                                                 break;
  632.                                             }
  633.  
  634.                                             SomeNode = SomeNode -> ln_Succ;
  635.                                         }
  636.  
  637.                                         Close(SomeFile);
  638.  
  639.                                         if(GetFileSize(DummyBuffer))
  640.                                         {
  641.                                             AddProtection(DummyBuffer,FIBF_EXECUTE);
  642.  
  643.                                             if(Config -> MiscConfig -> CreateIcons)
  644.                                                 AddIcon(DummyBuffer,FILETYPE_TEXT,TRUE);
  645.                                         }
  646.                                         else
  647.                                             DeleteFile(DummyBuffer);
  648.  
  649.                                         ReleaseSemaphore(&PacketSemaphore);
  650.                                     }
  651.  
  652.                                     if(Error)
  653.                                         ShowError(PacketWindow,ERR_SAVE_ERROR,Error,DummyBuffer);
  654.  
  655.                                     FreeAslRequest(FileRequest);
  656.                                 }
  657.                             }
  658.  
  659.                             LT_UnlockWindow(PacketWindow);
  660.                             break;
  661.  
  662.                         case MEN_CLEARHISTORY:
  663.  
  664.                             LT_LockWindow(PacketWindow);
  665.  
  666.                             ClearPacketHistory();
  667.  
  668.                             LT_UnlockWindow(PacketWindow);
  669.                             break;
  670.  
  671.                         case MEN_OTHERWINDOW:
  672.  
  673.                             SwapWindow = TRUE;
  674.                             break;
  675.                     }
  676.  
  677.                     MsgCode = MenuItem -> NextSelect;
  678.                 }
  679.  
  680.                 ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  681.             }
  682.  
  683.                 /* Shut down. */
  684.  
  685.             if(MsgClass == IDCMP_CLOSEWINDOW)
  686.             {
  687.                 Forbid();
  688.  
  689.                 if(!(SetSignal(0,SIG_KILL) & SIG_KILL))
  690.                     return(FALSE);
  691.                 else
  692.                     return(TRUE);
  693.             }
  694.  
  695.                 /* Activate the string gadget as well. */
  696.  
  697.             if(MsgClass == IDCMP_ACTIVEWINDOW)
  698.                 ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  699.  
  700.             if(MsgClass == IDCMP_NEWSIZE)
  701.             {
  702.                 PacketWindow -> Flags |= WFLG_RMBTRAP;
  703.  
  704.                 strcpy(DummyBuffer,PacketContents);
  705.  
  706.                 RemoveGList(PacketWindow,PacketGadgetList,(UWORD)-1);
  707.  
  708.                 FreeGadgets(PacketGadgetList);
  709.  
  710.                 PacketGadgetList = NULL;
  711.  
  712.                 SetAPen(PacketWindow -> RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
  713.                 RectFill(PacketWindow -> RPort,PacketWindow -> BorderLeft,PacketWindow -> BorderTop,PacketWindow -> Width - PacketWindow -> BorderRight,PacketWindow -> Height - PacketWindow -> BorderBottom);
  714.                 RefreshWindowFrame(PacketWindow);
  715.  
  716.                 PacketHeight    = PacketWindow -> Height;
  717.                 PacketWidth    = PacketWindow -> Width;
  718.  
  719.                 ObtainSemaphore(&PacketSemaphore);
  720.  
  721.                 if(CreateAllGadgets(PacketWindow -> Width,PacketGadgetArray,&PacketGadgetList,VisualInfo))
  722.                 {
  723.                     PacketContents = ((struct StringInfo *)PacketGadgetArray[GAD_STRING] -> SpecialInfo) -> Buffer;
  724.  
  725.                     AddGList(PacketWindow,PacketGadgetList,(UWORD)-1,(UWORD)-1,NULL);
  726.                     RefreshGList(PacketGadgetList,PacketWindow,NULL,(UWORD)-1);
  727.                     GT_RefreshWindow(PacketWindow,NULL);
  728.  
  729.                     ReleaseSemaphore(&PacketSemaphore);
  730.  
  731.                     PacketWindow -> Flags &= ~WFLG_RMBTRAP;
  732.  
  733.                     GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
  734.                         GTST_String,    DummyBuffer,
  735.                     TAG_DONE);
  736.  
  737.                     ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  738.                 }
  739.                 else
  740.                 {
  741.                     DisplayBeep(PacketWindow -> WScreen);
  742.  
  743.                     ReleaseSemaphore(&PacketSemaphore);
  744.  
  745.                     if(!(SetSignal(0,SIG_KILL) & SIG_KILL))
  746.                         return(FALSE);
  747.                     else
  748.                         return(TRUE);
  749.                 }
  750.             }
  751.  
  752.             if(MsgClass == IDCMP_MOUSEBUTTONS)
  753.                 ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  754.  
  755.                 /* User has entered a string. */
  756.  
  757.             if(MsgClass == IDCMP_GADGETUP)
  758.             {
  759.                 switch(MsgGadget -> GadgetID)
  760.                 {
  761.                     case GAD_STRING:
  762.  
  763.                         if(MsgCode != '\t')
  764.                         {
  765.                                 /* Is there anything in the buffer at all? */
  766.  
  767.                             if(PacketContents[0])
  768.                             {
  769.                                 strcpy(DummyBuffer,PacketContents);
  770.  
  771.                                 if(PacketString)
  772.                                 {
  773.                                     if(strcmp(PacketString,DummyBuffer))
  774.                                         AddPacketHistory(DummyBuffer);
  775.  
  776.                                     PacketString = NULL;
  777.                                 }
  778.                                 else
  779.                                     AddPacketHistory(DummyBuffer);
  780.  
  781.                                     /* Convert alien IBM characters. */
  782.  
  783.                                 if(Config -> TerminalConfig -> FontMode == FONT_IBM)
  784.                                 {
  785.                                     UBYTE Char;
  786.                                     WORD i;
  787.  
  788.                                     for(i = 0 ; i < strlen(DummyBuffer) ; i++)
  789.                                     {
  790.                                         if(Char = IBMConversion[DummyBuffer[i]])
  791.                                             DummyBuffer[i] = Char;
  792.                                     }
  793.                                 }
  794.  
  795.                                     /* Execute the command. */
  796.  
  797.                                 PacketSerialCommand(DummyBuffer);
  798.                             }
  799.  
  800.                                 /* Clear the packet window string
  801.                                  * gadget.
  802.                                  */
  803.  
  804.                             GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
  805.                                 GTST_String,    "",
  806.                             TAG_DONE);
  807.  
  808.                                 /* Send a terminating `CR'. */
  809.  
  810.                             switch(Config -> TerminalConfig -> SendCR)
  811.                             {
  812.                                 case EOL_CR:
  813.  
  814.                                     PacketSerWrite("\r",1);
  815.                                     break;
  816.  
  817.                                 case EOL_LF:
  818.  
  819.                                     PacketSerWrite("\n",1);
  820.                                     break;
  821.  
  822.                                 case EOL_CRLF:
  823.  
  824.                                     PacketSerWrite("\r\n",2);
  825.                                     break;
  826.  
  827.                                 case EOL_LFCR:
  828.  
  829.                                     PacketSerWrite("\n\r",2);
  830.                                     break;
  831.                             }
  832.  
  833.                                 /* Re-activate the string gadget. */
  834.  
  835.                             ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  836.                         }
  837.  
  838.                         break;
  839.  
  840.                     case GAD_LIST:
  841.  
  842.                         ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  843.                         break;
  844.                 }
  845.             }
  846.  
  847.             if(SwapWindow)
  848.             {
  849.                 BumpWindow(Window);
  850.  
  851.                 SwapWindow = FALSE;
  852.             }
  853.         }
  854.  
  855.         if(PacketMsg = (struct PacketMessage *)GetMsg(PacketPort))
  856.         {
  857.             struct Node *Node;
  858.  
  859.             ObtainSemaphore(&PacketSemaphore);
  860.  
  861.             if(Node = GetListNode(PacketMsg -> Line,&PacketHistoryList))
  862.             {
  863.                 PacketString = Node -> ln_Name;
  864.  
  865.                 if(HasList)
  866.                 {
  867.                     GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
  868.                         GTLV_Selected,    PacketMsg -> Line,
  869.                         GTLV_Top,    PacketMsg -> Line,
  870.                     TAG_DONE);
  871.                 }
  872.                 else
  873.                 {
  874.                     GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
  875.                         GTST_String,    Node -> ln_Name,
  876.                     TAG_DONE);
  877.                 }
  878.             }
  879.             else
  880.             {
  881.                 PacketString = NULL;
  882.  
  883.                 GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
  884.                     GTST_String,    "",
  885.                 TAG_DONE);
  886.             }
  887.  
  888.             ReleaseSemaphore(&PacketSemaphore);
  889.  
  890.             PacketCount = PacketMsg -> Line;
  891.  
  892.             FreeVecPooled(PacketMsg);
  893.  
  894.             ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  895.         }
  896.     }
  897. }
  898.  
  899. STATIC VOID __saveds
  900. PacketProcessEntry(VOID)
  901. {
  902.     BOOLEAN RingBack = TRUE;
  903.  
  904.     if((PacketSignal = AllocSignal(-1)) != -1)
  905.     {
  906.  
  907.         if(PacketPort = (struct MsgPort *)AllocVecPooled(sizeof(struct MsgPort),MEMF_PUBLIC | MEMF_CLEAR))
  908.         {
  909.             struct Message *Message;
  910.  
  911.             if(PacketMenu = CreateMenus(NewPacketMenu,TAG_DONE))
  912.             {
  913.                 if(LayoutMenus(PacketMenu,VisualInfo,
  914.                     GTMN_TextAttr,        &UserFont,
  915.                     GTMN_NewLookMenus,    TRUE,
  916.  
  917.                     AmigaGlyph ? GTMN_AmigaKey :  TAG_IGNORE, AmigaGlyph,
  918.                     CheckGlyph ? GTMN_Checkmark : TAG_IGNORE, CheckGlyph,
  919.                 TAG_DONE))
  920.                 {
  921.                     LONG ProtoHeight;
  922.  
  923.                     ProtoHeight = Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 1 + 1 + 3 + TextAttr . tta_YSize + 3 + 1 + Window -> WScreen -> WBorBottom;
  924.  
  925.                     if(PacketWidth == -1)
  926.                         PacketWidth = Window -> Width;
  927.  
  928.                     if(PacketHeight < ProtoHeight)
  929.                         PacketHeight = ProtoHeight;
  930.  
  931.                     if(PacketX == -1 || (PacketX + PacketWidth > Window -> WScreen -> Width))
  932.                     {
  933.                         PacketX = Window -> LeftEdge;
  934.  
  935.                         if(PacketX + PacketWidth > Window -> WScreen -> Width)
  936.                             PacketWidth = Window -> WScreen -> Width - PacketX;
  937.                     }
  938.  
  939.                     if(PacketY == -1 || (PacketY + PacketHeight > Window -> WScreen -> Height))
  940.                     {
  941.                         PacketY = Window -> TopEdge + Window -> Height;
  942.  
  943.                         if(PacketY + PacketHeight > Window -> WScreen -> Height)
  944.                             PacketHeight = ProtoHeight;
  945.                     }
  946.  
  947.                     if(CreateAllGadgets(PacketWidth,PacketGadgetArray,&PacketGadgetList,VisualInfo))
  948.                     {
  949.                         if(PacketWindow = OpenWindowTags(NULL,
  950.                             WA_Width,        PacketWidth,
  951.                             WA_Height,        PacketHeight,
  952.  
  953.                             WA_Left,        PacketX,
  954.                             WA_Top,            PacketY,
  955.  
  956.                             WA_Activate,        TRUE,
  957.                             WA_DragBar,        TRUE,
  958.                             WA_DepthGadget,        TRUE,
  959.                             WA_CloseGadget,        TRUE,
  960.                             WA_RMBTrap,        TRUE,
  961.                             WA_SizeGadget,        TRUE,
  962.                             WA_MinWidth,        80,
  963.                             WA_MinHeight,        ProtoHeight,
  964.                             WA_MaxWidth,        Window -> WScreen -> Width,
  965.                             WA_MaxHeight,        Window -> WScreen -> Height,
  966.                             WA_CustomScreen,    Window -> WScreen,
  967. //                            WA_NoCareRefresh,    TRUE,
  968.                             WA_NewLookMenus,    TRUE,
  969.                             BackfillTag,        &BackfillHook,
  970.  
  971.                             WA_IDCMP,        STRINGIDCMP | LISTVIEWIDCMP | IDCMP_NEWSIZE | IDCMP_SIZEVERIFY | IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_ACTIVEWINDOW | IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_MENUHELP | IDCMP_REFRESHWINDOW,
  972.                             WA_MenuHelp,        TRUE,
  973.  
  974.                             WA_Title,        LocaleString(MSG_GLOBAL_PACKET_WINDOW_TXT),
  975.  
  976.                             AmigaGlyph ? WA_AmigaKey  : TAG_IGNORE, AmigaGlyph,
  977.                             CheckGlyph ? WA_Checkmark : TAG_IGNORE, CheckGlyph,
  978.                         TAG_DONE))
  979.                         {
  980.                             APTR OldPtr;
  981.  
  982.                             SetAPen(PacketWindow -> RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
  983.                             SetDrMd(PacketWindow -> RPort,JAM1);
  984.                             RectFill(PacketWindow -> RPort,PacketWindow -> BorderLeft,PacketWindow -> BorderTop,PacketWindow -> Width - (PacketWindow -> BorderRight + 1),PacketWindow -> Height - (PacketWindow -> BorderBottom + 1));
  985.  
  986.                             PacketPort -> mp_Flags        = PA_SIGNAL;
  987.                             PacketPort -> mp_SigBit        = PacketWindow -> UserPort -> mp_SigBit;
  988.                             PacketPort -> mp_SigTask    = PacketWindow -> UserPort -> mp_SigTask;
  989.  
  990.                             NewList(&PacketPort -> mp_MsgList);
  991.  
  992.                             PacketHook . h_Entry        = (LONG (*)())PacketKey;
  993.                             PacketHook . h_SubEntry        = NULL;
  994.                             PacketHook . h_Data        = NULL;
  995.  
  996.                             PacketContents = ((struct StringInfo *)PacketGadgetArray[GAD_STRING] -> SpecialInfo) -> Buffer;
  997.  
  998.                             AddGList(PacketWindow,PacketGadgetList,(UWORD)-1,(UWORD)-1,NULL);
  999.                             RefreshGList(PacketGadgetList,PacketWindow,NULL,(UWORD)-1);
  1000.                             GT_RefreshWindow(PacketWindow,NULL);
  1001.  
  1002.                             SetMenuStrip(PacketWindow,PacketMenu);
  1003.  
  1004.                             PacketWindow -> Flags &= ~WFLG_RMBTRAP;
  1005.  
  1006.                             ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
  1007.  
  1008.                             PacketProcess = (struct Process *)FindTask(NULL);
  1009.  
  1010.                             OldPtr = PacketProcess -> pr_WindowPtr;
  1011.  
  1012.                             PacketProcess -> pr_WindowPtr = PacketWindow;
  1013.  
  1014.                             Signal(ThisProcess,SIG_HANDSHAKE);
  1015.  
  1016.                             RingBack = HandlePacket();
  1017.  
  1018.                             PacketProcess -> pr_WindowPtr = OldPtr;
  1019.  
  1020.                             PacketWindow -> Flags |= WFLG_RMBTRAP;
  1021.  
  1022.                             ClearMenuStrip(PacketWindow);
  1023.  
  1024.                             RemoveGList(PacketWindow,PacketGadgetList,(UWORD)-1);
  1025.  
  1026.                             PacketX        = PacketWindow -> LeftEdge;
  1027.                             PacketY        = PacketWindow -> TopEdge;
  1028.  
  1029.                             PacketWidth    = PacketWindow -> Width;
  1030.                             PacketHeight    = PacketWindow -> Height;
  1031.  
  1032.                             LT_DeleteWindowLock(PacketWindow);
  1033.  
  1034.                             CloseWindow(PacketWindow);
  1035.  
  1036.                             PacketWindow = NULL;
  1037.                         }
  1038.  
  1039.                         FreeGadgets(PacketGadgetList);
  1040.  
  1041.                         PacketGadgetList = NULL;
  1042.                     }
  1043.                 }
  1044.  
  1045.                 FreeMenus(PacketMenu);
  1046.  
  1047.                 PacketMenu = NULL;
  1048.             }
  1049.  
  1050.             while(Message = GetMsg(PacketPort))
  1051.                 FreeVecPooled(Message);
  1052.  
  1053.             FreeVecPooled(PacketPort);
  1054.  
  1055.             PacketPort = NULL;
  1056.         }
  1057.  
  1058.         FreeSignal(PacketSignal);
  1059.  
  1060.         PacketSignal = -1;
  1061.     }
  1062.  
  1063.     Forbid();
  1064.  
  1065.     PacketProcess = NULL;
  1066.  
  1067.     if(RingBack)
  1068.         Signal(ThisProcess,SIG_HANDSHAKE);
  1069.     else
  1070.         CheckItem(MEN_PACKET_WINDOW,FALSE);
  1071. }
  1072.  
  1073.     /* ClearPacketHistory():
  1074.      *
  1075.      *    Release the command history.
  1076.      */
  1077.  
  1078. VOID
  1079. ClearPacketHistory()
  1080. {
  1081.     if(PacketProcess)
  1082.     {
  1083.         ObtainSemaphore(&PacketSemaphore);
  1084.  
  1085.         if(HasList)
  1086.         {
  1087.             GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
  1088.                 GTLV_Labels,~0,
  1089.             TAG_DONE);
  1090.         }
  1091.  
  1092.         FreeList(&PacketHistoryList);
  1093.  
  1094.         PacketCount = PacketLine = 0;
  1095.  
  1096.         if(HasList)
  1097.         {
  1098.             GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
  1099.                 GTLV_Labels,&PacketHistoryList,
  1100.             TAG_DONE);
  1101.         }
  1102.  
  1103.         PacketString = NULL;
  1104.  
  1105.         ReleaseSemaphore(&PacketSemaphore);
  1106.     }
  1107. }
  1108.  
  1109.     /* AddPacketHistory(STRPTR Buffer):
  1110.      *
  1111.      *    Add a line to the packet window command history. This
  1112.      *    works very much the same as the AddLine()-routine.
  1113.      */
  1114.  
  1115. VOID __regargs
  1116. AddPacketHistory(STRPTR Buffer)
  1117. {
  1118.     if(PacketProcess)
  1119.     {
  1120.         struct Node *SomeNode;
  1121.  
  1122.         ObtainSemaphore(&PacketSemaphore);
  1123.  
  1124.         if(HasList)
  1125.         {
  1126.             GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
  1127.                 GTLV_Labels,    ~0,
  1128.             TAG_DONE);
  1129.         }
  1130.  
  1131.         if(SomeNode = CreateNode(Buffer))
  1132.         {
  1133.             AddTail(&PacketHistoryList,SomeNode);
  1134.  
  1135.             PacketCount = ++PacketLine;
  1136.         }
  1137.  
  1138.         if(HasList)
  1139.         {
  1140.             GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
  1141.                 GTLV_Top,    PacketCount - 1,
  1142.                 GTLV_Labels,    &PacketHistoryList,
  1143.             TAG_DONE);
  1144.         }
  1145.  
  1146.         ReleaseSemaphore(&PacketSemaphore);
  1147.     }
  1148. }
  1149.  
  1150.     /* DeletePacketWindow():
  1151.      *
  1152.      *    Delete the packet window and release the command
  1153.      *    history.
  1154.      */
  1155.  
  1156. VOID __regargs
  1157. DeletePacketWindow(BYTE WindowOnly)
  1158. {
  1159.     CheckItem(MEN_PACKET_WINDOW,FALSE);
  1160.  
  1161.     if(PacketProcess)
  1162.     {
  1163.         Forbid();
  1164.  
  1165.         ClrSignal(SIG_HANDSHAKE);
  1166.  
  1167.         Signal(PacketProcess,SIG_KILL);
  1168.  
  1169.         Wait(SIG_HANDSHAKE);
  1170.  
  1171.         Permit();
  1172.  
  1173.         if(!WindowOnly)
  1174.         {
  1175.             HasList = FALSE;
  1176.  
  1177.             FreeList(&PacketHistoryList);
  1178.  
  1179.             PacketCount = PacketLine = 0;
  1180.  
  1181.             PacketString = NULL;
  1182.         }
  1183.     }
  1184. }
  1185.  
  1186.     /* CreatePacketWindow():
  1187.      *
  1188.      *    Open the packet window and allocate the command history
  1189.      *    buffer.
  1190.      */
  1191.  
  1192. BYTE
  1193. CreatePacketWindow()
  1194. {
  1195.     if(Echo == -1)
  1196.         Echo = Config -> SerialConfig -> Duplex;
  1197.  
  1198.     if(!PacketProcess)
  1199.     {
  1200.         InitSemaphore(&PacketSemaphore);
  1201.  
  1202.         LocalizeMenu(NewPacketMenu,MSG_PACKET_PROJECT_MEN);
  1203.  
  1204.         Forbid();
  1205.  
  1206.         if(CreateNewProcTags(
  1207.             NP_Name,    "term Packet Process",
  1208.             NP_Priority,    SysBase -> ThisTask -> tc_Node . ln_Pri,
  1209.             NP_Entry,    PacketProcessEntry,
  1210.             NP_StackSize,    6000,
  1211.         TAG_DONE))
  1212.         {
  1213.             ClrSignal(SIG_HANDSHAKE);
  1214.  
  1215.             Wait(SIG_HANDSHAKE);
  1216.         }
  1217.  
  1218.         Permit();
  1219.     }
  1220.  
  1221.     if(PacketProcess)
  1222.     {
  1223.         Forbid();
  1224.  
  1225.         ActivateWindow(PacketWindow);
  1226.  
  1227.         CheckItem(MEN_PACKET_WINDOW,TRUE);
  1228.  
  1229.         Permit();
  1230.  
  1231.         return(TRUE);
  1232.     }
  1233.     else
  1234.     {
  1235.         CheckItem(MEN_PACKET_WINDOW,FALSE);
  1236.  
  1237.         return(FALSE);
  1238.     }
  1239. }
  1240.